using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using sharpsvr.common;
using sharpsvr.core;
using sharpsvr.net;
using sharpsvr.util;

namespace sharpsvr.protocol
{
    public class SharpProtocol : Singleton<SharpProtocol>
    {
        public const string SVR0 = "svr0";

        public const string SVR = SVR0;

        public const int HEADER_LEN = 20;

        private static ILog log = Logger.GetInstance().GetLog();

        private Random random = new Random();

        public static string GetVersion(Type type)
        {
            var attribute = AttributeUtils.GetRemoteServiceAttribute(type);
            return attribute == null ? "1.0.0" : attribute.Version;
        }

        public static byte[] GetHeader(byte[] content)
        {
            uint len = (uint)content.Length;
            byte[] header = new byte[HEADER_LEN];
            var index = 0;
            string headerStr = SVR + SharpSvrSettings.GetInstance().EncoderName;
            header[0] = (byte)headerStr.Length;
            for (; index < headerStr.Length; ++index)
            {
                header[index + 1] = (byte)headerStr[index];
            }
            header[HEADER_LEN - 4] = (byte)(len & 0xff);
            header[HEADER_LEN - 3] = (byte)((len >> 8) & 0xff);
            header[HEADER_LEN - 2] = (byte)((len >> 16) & 0xff);
            header[HEADER_LEN - 1] = (byte)((len >> 24) & 0xff);
            return header;
        }


        public byte[] Encode(MethodInfo methodInfo, object[] args)
        {
            return Encode(new SharpPacket
            {
                Version = GetVersion(methodInfo.DeclaringType),
                ServiceUniqueName = TypeUtils.GetTypeUniqueName(methodInfo.DeclaringType),
                MethodUniqueName = TypeUtils.GetMethodUniqueName(methodInfo),
                Arguments = args,
                Random = random.Next()
            }, false);
        }

        public byte[] Encode(SharpPacket packet, bool isServerSide = true)
        {
            if (packet.Random == 0) packet.Random = random.Next();
            var serializer = SerializerManager.GetInstance().GetSerializer(SharpSvrSettings.GetInstance().EncoderName);
            //服务端向客户端发送,不必要数据置空,减少浏览
            if (isServerSide)
            {
                packet.MethodUniqueName = packet.ServiceUniqueName = packet.Version = null;
                packet.Arguments = null;
            }
            return serializer.Serialize(packet);
        }

        public SharpPacket TryDecodePacket(SocketSession socketSession)
        {
            if (socketSession.RecvBuffer.Used < HEADER_LEN) return null;
            byte headerStrLen = socketSession.RecvBuffer[0];
            for (int i = 0; i < SVR.Length; ++i)
            {
                if (socketSession.RecvBuffer[i + 1] != SVR[i])
                {
                    log.Error("recv buffer error! protocol header check failed!");
                    socketSession.Close();
                    return null;

                }
            }
            byte[] header = null;
            socketSession.RecvBuffer.PeekBytes(ref header, HEADER_LEN);

            string encoder = System.Text.Encoding.ASCII.GetString(header, SVR.Length + 1, headerStrLen - SVR.Length);
            var fullPacketSize = (socketSession.RecvBuffer[HEADER_LEN - 4]) + (socketSession.RecvBuffer[HEADER_LEN - 3] << 8) +
            (socketSession.RecvBuffer[HEADER_LEN - 2] << 16) + (socketSession.RecvBuffer[HEADER_LEN - 1] << 24) + HEADER_LEN;
            if (fullPacketSize <= socketSession.RecvBuffer.Used)
            {
                var buffer = ByteArrayFactory.GetInstance().allocate((uint)(fullPacketSize));
                socketSession.RecvBuffer.ReadBytes(ref buffer, (uint)fullPacketSize);
                var serializer = SerializerManager.GetInstance().GetSerializer(encoder);
                var obj = serializer.Deserialize<SharpPacket>(buffer, HEADER_LEN, fullPacketSize);
                (obj as SharpPacket).Encoder = encoder;
                log.Debug($"recv packet:{obj}, client session:{socketSession.BindSocket.Handle}");
                return obj as SharpPacket;
            }
            return null;
        }

    }
}